package com.osmino.contacts.provider;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import android.content.Context;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Process;
import com.osmino.contacts.entity.Contact;
import com.osmino.contacts.entity.Group;
import com.osmino.contacts.entity.QueryRestrictions;
import com.osmino.contacts.entity.RawContact;

public final class AsyncContactsCursorLoader extends AsyncTask<Void, Void, Void> {

	private final ContactsContractResolver mContactsResolver;
	private Map<Long, Contact> mContacts;
	private Map<Long, Group> mGroups;

	public AsyncContactsCursorLoader(ContactsContractResolver contactsResolver) {
		mContactsResolver = contactsResolver;
	}

	@Override
	protected void onPreExecute() {
		mContacts = new HashMap<Long, Contact>();
		mGroups = new HashMap<Long, Group>();
		mContactsResolver.onStartRefresh();
	}

	@Override
	protected Void doInBackground(Void... params) {
		Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
		System.gc();
		Map<Long, RawContact> rawContacts = new HashMap<Long, RawContact>();
		Context context = mContactsResolver.getContext();
		QueryRestrictions queryRestrictions = mContactsResolver.getQueryRestrictions();
		Cursor rawContactsCursor = ContactsContractQueries.getAllRawContacts(context);
		Cursor rawContactsDataCursor = ContactsContractQueries.getListData(context);
		Cursor contactsCursor = ContactsContractQueries.getListContacts(context);
		Cursor groupCursor = ContactsContractQueries.getListGroups(context);
		fillRawContacts(rawContacts, context, rawContactsCursor, rawContactsDataCursor, queryRestrictions);
		fillContacts(mContacts, context, contactsCursor, rawContacts, queryRestrictions);
		fillGroups(mGroups, context, groupCursor, queryRestrictions);
		contactsCursor.close();
		rawContactsDataCursor.close();
		rawContactsCursor.close();
		groupCursor.close();
		return null;
	}

	private static void fillRawContacts(Map<Long, RawContact> rawContacts, Context context, Cursor rawContactsCursor,
			Cursor rawContactsDataCursor, QueryRestrictions restrictions) {
		rawContactsCursor.moveToFirst();
		while (!rawContactsCursor.isAfterLast()) {
			RawContact rawContact = new RawContact(rawContactsCursor);
			if (restrictions.isAccountTypeEnabled(rawContact.account.type)) {
				rawContacts.put(rawContact.id, rawContact);
			} else {
				rawContact = null;
			}
			rawContactsCursor.moveToNext();
		}
		rawContactsDataCursor.moveToFirst();
		while (!rawContactsDataCursor.isAfterLast()) {
			int columnIndex = rawContactsDataCursor.getColumnIndex(RawContact.DATA_RAW_CONTACT_ID_COLUMN);
			long rawContactId = rawContactsDataCursor.getLong(columnIndex);
			RawContact rawContact = rawContacts.get(rawContactId);
			if (rawContact != null) {
				rawContact.attachData(context, rawContactsDataCursor);
			}
			rawContactsDataCursor.moveToNext();
		}
	}

	private static void fillContacts(Map<Long, Contact> contacts, Context context, Cursor contactsCursor,
			Map<Long, RawContact> rawContacts, QueryRestrictions restrictions) {
		Set<Long> permittedContactIds = new HashSet<Long>();
		for (RawContact rawContact : rawContacts.values()) {
			permittedContactIds.add(rawContact.contactId);
		}
		contactsCursor.moveToFirst();
		while (!contactsCursor.isAfterLast()) {
			int columnIndex = contactsCursor.getColumnIndex(Contact.ID_COLUMN);
			long contactId = contactsCursor.getLong(columnIndex);
			Contact contact = new Contact(context, contactsCursor, Contact.MASK_ALL);
			if (permittedContactIds.contains(contactId) && restrictions.isContactEnabled(contact)) {
				contacts.put(contactId, contact);
			}
			contactsCursor.moveToNext();
		}
		for (RawContact rawContact : rawContacts.values()) {
			Contact contact = contacts.get(rawContact.contactId);
			if (contact != null) {
				contact.addRawContact(rawContact);
			}
		}
	}

	private static void fillGroups(Map<Long, Group> groups, Context context, Cursor groupCursor,
			QueryRestrictions restrictions) {
		groupCursor.moveToFirst();
		while (!groupCursor.isAfterLast()) {
			Group group = new Group(context, groupCursor);
			groups.put(group.id, group);
			groupCursor.moveToNext();
		}
		groups.put(Group.NO_GROUP, Group.createUngroupedGroup(context));
	}

	@Override
	protected void onPostExecute(Void result) {
		mContactsResolver.onDataChanged(mContacts, mGroups);
	}
}
